﻿

using System.Collections.Generic;
using UnityEngine;
using SimpleJson;
using System;

namespace ABA
{
    public enum E_TIMER_TYPE
    {
        UPDATE = 1,
        COOLDOWN = 2,
        TIMER = 3
    }

    public enum E_TIMER_UPDATE_TYPE
    {
        NONE,
        UPDATE,
        FIXEDUPDATE
    }

    public delegate void TimerCallback(JsonObject param);
    /// <summary>
    /// 定时器， update， 定时执行
    /// </summary>
    public class Timer : MonoBehaviour
    {
        public static int repeat;
        private static long timeStamp => System.DateTimeOffset.Now.ToUnixTimeMilliseconds();
        Dictionary<long, Anymous_T> _items;
        Dictionary<long, Anymous_T> _toAdd;
        List<Anymous_T> _toRemove;
        List<Anymous_T> _pool;

        E_TIMER_UPDATE_TYPE _updateType = E_TIMER_UPDATE_TYPE.UPDATE;

        private void Awake() {
            _items = new Dictionary<long, Anymous_T>();
            _toAdd = new Dictionary<long, Anymous_T>();
            _toRemove = new List<Anymous_T>();
            _pool = new List<Anymous_T>(100);
        }

        // 定时器
        // public TimerEx()
        // {
        //     _items = new Dictionary<long, AnymousEx_T>();
        //     _toAdd = new Dictionary<long, AnymousEx_T>();
        //     _toRemove = new List<AnymousEx_T>();
        //     _pool = new List<AnymousEx_T>(100);
        // }

        public void SetUpdateType(E_TIMER_UPDATE_TYPE updateType)
        {
            _updateType = updateType;
        }

        // 每帧执行
        public long AddUpdate(TimerCallback update){
            return _Add(0, 0, null, update, null, E_TIMER_TYPE.UPDATE);            
        }

        // 只支持一次, delay 时间到了，会清除 finish, update
        public long AddCD_Update(float delay, TimerCallback finish, TimerCallback update){
            return _Add(delay, 1, finish, update, null, E_TIMER_TYPE.COOLDOWN);            
        }

        // 添加定时器，id 为key  interval 单位s
        public long Add(float interval, int repeat, TimerCallback callback)
        {
            return Add(interval, repeat, callback, null);
        }

        /**
         * @interval in seconds
         * @repeat 0 indicate loop infinitely, otherwise the run count
         **/
        public long Add(float interval, int repeat, TimerCallback callback, JsonObject callbackParam){
            return _Add(interval, repeat, callback, null, callbackParam, E_TIMER_TYPE.TIMER);
        }

        private long _Add(float interval, int repeat, TimerCallback callback, TimerCallback update, JsonObject callbackParam, E_TIMER_TYPE type){
            long id = timeStamp;

            if(_items.ContainsKey(id)){
                for(int i=1; i<=500; i++){
                    id += i;
                    if(!_items.ContainsKey(id)){
                        break;
                    }
                }
            }

            if(_toAdd.ContainsKey(id)){
                for(int i=1; i<=500; i++){
                    id += i;
                    if(!_toAdd.ContainsKey(id)){
                        break;
                    }
                }
            }

            Anymous_T t;
            t = GetFromPool();
            t.id = id;
            t.type = type;
            t.interval = interval;
            t.repeat = repeat;
            t.callback = callback;
            t.update = update;
            t.param = callbackParam;
            _toAdd[id] = t;

            return id;
        }


        public long CallLater(TimerCallback callback)
        {
            return Add(0.001f, 1, callback);
        }

        public long CallLater(TimerCallback callback, JsonObject callbackParam)
        {
            return Add(0.001f, 1, callback, callbackParam);
        }

        public bool Exists(long id)
        {
            if (_toAdd.ContainsKey(id))
                return true;

            Anymous_T at;
            if (_items.TryGetValue(id, out at))
                return !at.deleted;

            return false;
        }

        public void Pause(long id)
        {
            Anymous_T t;
            if (_toAdd.TryGetValue(id, out t))
            {
                t.running = false;
            }
        }

        public void Resume(long id)
        {
            Anymous_T t;
            if (_toAdd.TryGetValue(id, out t))
            {
                t.running = true;
            }
        }

        // 移除定时器, 通过Add的时候的id
        public void Remove(long id)
        {
            Anymous_T t;
            if (_toAdd.TryGetValue(id, out t))
            {
                _toAdd.Remove(id);
                ReturnToPool(t);
            }

            if (_items.TryGetValue(id, out t))
                t.deleted = true;
        }

        private Anymous_T GetFromPool()
        {
            Anymous_T t;
            int cnt = _pool.Count;
            if (cnt > 0)
            {
                t = _pool[cnt - 1];
                _pool.RemoveAt(cnt - 1);
                t.callback = null;
                t.update = null;
                t.type = E_TIMER_TYPE.TIMER;
                t.param = null;
                t.deleted = false;
                t.elapsed = 0;
                t.running = true;
            }
            else
                t = new Anymous_T();
            return t;
        }

        private void ReturnToPool(Anymous_T t)
        {
            t.callback = null;
            t.running = false;
            _pool.Add(t);
        }

        // 固定Time.fixedDeltaTime 0.02, 调用频率受timeScale 影响
        private void FixedUpdate() {
            if(_updateType != E_TIMER_UPDATE_TYPE.FIXEDUPDATE) return;

            _Update(Time.fixedDeltaTime);            
        }

        // 调用频率不受timeScale 影响，但 Time.deltaTime 受 timeScale 影响
        private void Update() {
            if(_updateType != E_TIMER_UPDATE_TYPE.UPDATE) return;

            _Update(Time.deltaTime);
        }

        private void _Update(float dt)
        {
            // float dt = Time.deltaTime;
            Dictionary<long, Anymous_T>.Enumerator iter;
            if (_items.Count > 0)
            {
                iter = _items.GetEnumerator();
                while (iter.MoveNext())
                {
                    Anymous_T i = iter.Current.Value;
                    if(i.running == false){
                        continue;
                    }

                    if (i.deleted)
                    {
                        _toRemove.Add(i);
                        continue;
                    }

                    if(i.type == E_TIMER_TYPE.UPDATE){
                        if(i.update != null) i.update(i.param);
                        continue;
                    }
                    else if(i.type == E_TIMER_TYPE.COOLDOWN){
                        if(i.update != null) i.update(i.param);                            
                    }

                    i.elapsed += dt;
                    if (i.elapsed < i.interval)
                        continue;

                    i.elapsed -= i.interval;
                    if (i.elapsed < 0 || i.elapsed > 0.03f)
                        i.elapsed = 0;

                    if (i.repeat > 0)
                    {
                        i.repeat--;
                        if (i.repeat == 0)
                        {
                            i.deleted = true;
                            _toRemove.Add(i);
                        }
                    }
                    repeat = i.repeat;
                    if (i.callback != null)
                    {
                        i.callback(i.param);
                    }
                }
                iter.Dispose();
            }

            int len = _toRemove.Count;
            if (len > 0)
            {
                for (int k = 0; k < len; k++)
                {
                    Anymous_T i = _toRemove[k];
                    if (i.deleted && i.callback != null)
                    {
                        _items.Remove(i.id);
                        ReturnToPool(i);
                    }
                }
                _toRemove.Clear();
            }

            if (_toAdd.Count > 0)
            {
                iter = _toAdd.GetEnumerator();
                while (iter.MoveNext()){
                    if(!_items.ContainsKey(iter.Current.Key)){
                        _items.Add(iter.Current.Key, iter.Current.Value);
                    }
                    //_items.Add(iter.Current.Key, iter.Current.Value);
                }
                iter.Dispose();
                _toAdd.Clear();
            }
        }
    }

    class Anymous_T
    {
        public long id;
        public E_TIMER_TYPE type = E_TIMER_TYPE.TIMER;
        public float interval;
        public int repeat;
        
        public TimerCallback callback;
        public TimerCallback update;
        public JsonObject param;

        public float elapsed;
        public bool deleted;
        public bool running = true;
    }
}




